/*
* Nettle Source File.
* Copyright (C) 2013, Nettle (http://nttl.ru/).
*
* This file is part of NettleMath.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl-2.1.html
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Description:
*
* History:
* Created by Sergey Serb, l-proger@yandex.ru
*/

#ifndef NVectorBase_h__
#define NVectorBase_h__

#include "NMathTypes.h"
#include <assert.h>
#include <limits>
#include "NMathUtils.h"

namespace NettleMath{
    template<typename T, int SIZE>
    struct NVectorBase{
        typedef T element_type;
        enum{size = SIZE};

        /*void setVector(const T& s){
            T* data = reinterpret_cast<T*>(this);
            for(int i = 0; i < SIZE; i++){
                data[i] = s;
            }
        }

        T length(){
            T* data = reinterpret_cast<T*>(this);
            T sum = data[0] * data[0];
            for(int i = 1; i < SIZE; i++){
                sum += data[i] * data[i];
            }
            return static_cast<T>(sqrt(sum));
        }

        T lengthSquared(){
            T* data = reinterpret_cast<T*>(this);
            T sum = data[0] * data[0];
            for(int i = 1; i < SIZE; i++){
                sum += data[i] * data[i];
            }
            return sum;
        }

        void normalize()
        {
            T* data = reinterpret_cast<T*>(this);
            T sum = data[0] * data[0];
            for(int i = 1; i < SIZE; i++){
                sum += data[i] * data[i];
            }

            if ( sum != 0 ){
                sum = static_cast<T>(1) / static_cast<T>(sqrt(sum));
                for(int i = 0; i < SIZE; i++){
                    data[i] *= sum;
                }
            }
        }*/

        const T& operator [] (int index) const {
            assert((index >= 0) && (index < size));
            return reinterpret_cast<const T*>(this)[index];
        }

        T& operator [] (int index) {
            assert((index >= 0) && (index < size));
            return reinterpret_cast<T*>(this)[index];
        }

        operator const T* () const{
            return reinterpret_cast<const T*>(this);
        }

        operator T* (){
            return reinterpret_cast<T*>(this);
        }

        /*bool operator == (const NVectorBase<T,SIZE, DERIVED_TYPE>& v){
            for ( int i=0; i<SIZE; ++i ){
                if ( v[i] != (*this)[i] )
                    return false;
            }
            return true;
        }

        bool operator != (const NVectorBase<T,SIZE, DERIVED_TYPE>& v){
            for ( int i=0; i<SIZE; ++i ){
                if ( v[i] != (*this)[i] )
                    return true;
            }
            return false;
        }
        */
       /* DERIVED_TYPE operator + () const{
            return *static_cast<DERIVED_TYPE>(this);
        }
        DERIVED_TYPE operator - () const{
            DERIVED_TYPE val;
            const T* srcData = reinterpret_cast<const T*>(this);
            T* dstData = reinterpret_cast<T*>(&val);
            for(int i = 0; i < SIZE; i++){
                dstData[i] = -srcData[i];
            }
            return val;
        }

        DERIVED_TYPE operator + (const DERIVED_TYPE& v) const{
            DERIVED_TYPE result;
            const T* srcLeft = reinterpret_cast<const T*>(this);
            const T* srcRight = reinterpret_cast<const T*>(&v);
            T* dstData = reinterpret_cast<T*>(&result);
            for(int i = 0; i < SIZE; i++){
                dstData[i] = srcLeft[i] + srcRight[i];
            }
            return result;
        }

        DERIVED_TYPE operator - (const DERIVED_TYPE& v) const{
            DERIVED_TYPE result;
            const T* srcLeft = reinterpret_cast<const T*>(this);
            const T* srcRight = reinterpret_cast<const T*>(&v);
            T* dstData = reinterpret_cast<T*>(&result);
            for(int i = 0; i < SIZE; i++){
                dstData[i] = srcLeft[i] - srcRight[i];
            }
            return result;
        }

        DERIVED_TYPE operator * (const DERIVED_TYPE& v) const{
            DERIVED_TYPE result;
            const T* srcLeft = reinterpret_cast<const T*>(this);
            const T* srcRight = reinterpret_cast<const T*>(&v);
            T* dstData = reinterpret_cast<T*>(&result);
            for(int i = 0; i < SIZE; i++){
                dstData[i] = srcLeft[i] * srcRight[i];
            }
            return result;
        }

        DERIVED_TYPE operator * (const T& s) const{
            DERIVED_TYPE result;
            const T* srcLeft = reinterpret_cast<const T*>(this);
            T* dstData = reinterpret_cast<T*>(&result);
            for(int i = 0; i < SIZE; i++){
                dstData[i] = srcLeft[i] * s;
            }
            return result;
        }

        DERIVED_TYPE operator / (const T& s) const{
            //TODO: add float and double template specialization
            //to optimize divide by instead multiply on (1 / s)
            assert( s != 0 );
            DERIVED_TYPE result;
            const T* srcLeft = reinterpret_cast<const T*>(this);
            T* dstData = reinterpret_cast<T*>(&result);
            for(int i = 0; i < SIZE; i++){
                dstData[i] = srcLeft[i] / s;
            }
            return result;
        }

        DERIVED_TYPE& operator += (const DERIVED_TYPE& v){
            DERIVED_TYPE result;
            T* srcLeft = reinterpret_cast<T*>(this);
            const T* srcRight = reinterpret_cast<const T*>(&v);
            for(int i = 0; i < SIZE; i++){
                srcLeft[i] += srcRight[i];
            }
            return *static_cast<DERIVED_TYPE*>(this);
        }

        DERIVED_TYPE& operator -= (const DERIVED_TYPE& v){
            DERIVED_TYPE result;
            T* srcLeft = reinterpret_cast<T*>(this);
            const T* srcRight = reinterpret_cast<const T*>(&v);
            for(int i = 0; i < SIZE; i++){
                srcLeft[i] -= srcRight[i];
            }
            return *static_cast<DERIVED_TYPE*>(this);
        }

        DERIVED_TYPE& operator *= (const DERIVED_TYPE& v){
            DERIVED_TYPE result;
            T* srcLeft = reinterpret_cast<T*>(this);
            const T* srcRight = reinterpret_cast<const T*>(&v);
            for(int i = 0; i < SIZE; i++){
                srcLeft[i] *= srcRight[i];
            }
            return *static_cast<DERIVED_TYPE*>(this);
        }

        DERIVED_TYPE& operator *= (const T& s){
            DERIVED_TYPE result;
            T* srcLeft = reinterpret_cast<T*>(this);
            for(int i = 0; i < SIZE; i++){
                srcLeft[i] *= s;
            }
            return *static_cast<DERIVED_TYPE*>(this);
        }

        DERIVED_TYPE& operator /= (const T& s){
            assert( s != 0 );
            DERIVED_TYPE result;
            T* srcLeft = reinterpret_cast<T*>(this);
            for(int i = 0; i < SIZE; i++){
                srcLeft[i] /= s;
            }
            return *static_cast<DERIVED_TYPE*>(this);
        }

        DERIVED_TYPE& operator = (const DERIVED_TYPE& v){
            DERIVED_TYPE result;
            T* srcLeft = reinterpret_cast<T*>(this);
            const T* srcRight = reinterpret_cast<const T*>(&v);
            for(int i = 0; i < SIZE; i++){
                srcLeft[i] = srcRight[i];
            }
            return *static_cast<DERIVED_TYPE*>(this);
        }

        DERIVED_TYPE& operator = (const T& s){
            assert( s != 0 );
            DERIVED_TYPE result;
            T* srcLeft = reinterpret_cast<T*>(this);
            for(int i = 0; i < SIZE; i++){
                srcLeft[i] = s;
            }
            return *static_cast<DERIVED_TYPE*>(this);
        }*/
    };
}
#endif // NVectorBase_h__
